/** Easy parallelize job process manager.
 *
 *  Easy parallelize job process manager. Be used by the master node of easy parallel program
 *
 *  @author : huicong
 *  @date : 2008-11-16
 *
 */

#ifndef _EASY_PARALLEL_PROCESS_MANAGER_H
#define _EASY_PARALLEL_PROCESS_MANAGER_H

#include "BasicHeader.h"
#include "EasyParallelGroupManager.h"
#include <iostream>

namespace bcmi_frame
{

    class EasyParallelProcessManager
    {
    public:

        EasyParallelProcessManager(_IN EasyParallelGroupManager *_gManager):
          _groupManager(_gManager)
        {
            newArray(&_processStatus, _groupManager->processNumber());
            newArray(&_groupEmptyNumber, _groupManager->groupNumber());
            newArray(&_processSendList, _groupManager->processNumber());
            newArray(&_processCurrentTaskId, _groupManager->processNumber());
            newArray(&_processNextTaskId, _groupManager->processNumber());
            initialize();
        }

        ~EasyParallelProcessManager()
        {
            delete[] _processStatus;
            delete[] _groupEmptyNumber;
            for (UINT i = 0; i < _groupManager->processNumber(); ++i)
            {
                delete[] _processSendList[i];
            }
            delete[] _processSendList;
            delete[] _processCurrentTaskId;
            delete[] _processNextTaskId;
        }

    private:

        VOID initialize()
        {
            for (UINT i = 0 ;i < _groupManager->processNumber(); ++i)
            {
                _processStatus[i] = 0;
                _processCurrentTaskId[i] = 0;
                _processNextTaskId[i] = 0;
                newArray(&(_processSendList[i]), _groupManager->processNumber());
                memset(_processSendList[i], 0, _groupManager->processNumber());

            }
            _processStatus[bcmi_frame::MASTER_REMOTE_ID] = -1;
            for (UINT i = 0; i < _groupManager->groupNumber(); ++i)
            {
                _groupEmptyNumber[i] = _groupManager->groupSize(i);
            }
        }

    public:

        VOID parse(IN UINT _pid, IN BYTE *_requestBuf)
        {
            memcpy((BYTE*)&(_processCurrentTaskId[_pid]), _requestBuf, sizeof(UINT));
            memcpy((BYTE*)&(_processNextTaskId[_pid]), (_requestBuf + sizeof(UINT)), sizeof(UINT));
            memcpy(_processSendList[_pid], (_requestBuf + sizeof(UINT) * 2), _groupManager->processNumber());
        }

        INT currentTask(IN UINT _gid)
        {
            INT _tid = -1;
            for (UINT i = 0; i < _groupManager->processNumber(); ++i)
            {
                if (_groupManager->askGroup(i) == _gid)
                {
                    if (_tid == -1)
                    {
                        _tid = _processCurrentTaskId[i];
                    }
                    if (_tid != _processCurrentTaskId[i])
                    {
                        return -1;
                    }
                }
            }
            return _tid;
        }

        INT nextTask(IN UINT _gid)
        {
            INT _tid = -1;
            for (UINT i = 0; i < _groupManager->processNumber(); ++i)
            {
                if (_groupManager->askGroup(i) == _gid)
                {
                    if (_tid == -1)
                    {
                        _tid = _processNextTaskId[i];
                    }
                    if (_tid != _processNextTaskId[i])
                    {
                        return -1;
                    }
                }
            }
            return _tid;
        }

        /** Check if all message group-transfer is in _gid group */
        BOOL sendInGroup(IN UINT _gid)
        {
            for (UINT i = 0; i < _groupManager->processNumber(); ++i)
            {
                if (_groupManager->askGroup(i) == _gid)
                {
                    for (UINT j = 0; j < _groupManager->processNumber(); ++j)
                    {
                        if (sendTo(i, j) == (BYTE)1 && _groupManager->askGroup(j) != _gid)
                        {
                            return FALSE;
                        }
                    }
                }
            }
            return TRUE;
        }

        BYTE sendTo(IN UINT _pid, IN UINT _sendPid)
        {
            return _processSendList[_pid][_sendPid];
        }

        BOOL request(IN UINT _pid)
        {
            if (_pid < 0 || _pid >= _groupManager->processNumber() 
                || _processStatus[_pid] != 0)
            {
                return FALSE;
            }
            UINT _gid = _groupManager->askGroup(_pid);
            if (_gid < 0)
            {
                return FALSE;
            }
            _processStatus[_pid] = 1;
            --_groupEmptyNumber[_gid];
            return TRUE;
        }

        BOOL allSendRequest(IN UINT _gid)
        {
            if (_gid < 0 || _gid > _groupManager->groupNumber())
            {
                return FALSE;
            }
            return (_groupEmptyNumber[_gid] == 0);
        }

        VOID reponseRequest(IN UINT _pid)
        {
            _processStatus[_pid] = 0;
            ++_groupEmptyNumber[_groupManager->askGroup(_pid)];
        }

        VOID reponseFinished(IN UINT _pid)
        {
            _processStatus[_pid] = -1;
        }

        BOOL allProcessFinished()
        {
            for (UINT i = 0; i < _groupManager->processNumber(); ++i)
            {
                if (_processStatus[i] != -1)
                {
                    return FALSE;
                }
            }
            return TRUE;
        }

#ifndef DEBUG_EASY_PARALLEL_JOB
    private:
#else //#ifndef DEBUG_EASY_PARALLEL_JOB
    public:
#endif //#ifndef DEBUG_EASY_PARALLEL_JOB


        EasyParallelGroupManager *_groupManager;
        INT *_processStatus; // 0 : empty; 1 : send request; -1 : stop;
        INT *_groupEmptyNumber; // the number of process which status is zero;
        BYTE **_processSendList;
        UINT *_processCurrentTaskId;
        UINT *_processNextTaskId;

    };

}; //namespace bcmi_frame

#endif //#ifndef _EASY_PARALLEL_PROCESS_MANAGER_H
